#ifndef locking_h
#define locking_h
#include <pthread.h>
#include <set>

using namespace std;
typedef struct LOCK_st {
	pthread_mutex_t mutex;
	pthread_t thread;
	LOCK_st() {
		thread = 0;
		mutex = PTHREAD_MUTEX_INITIALIZER;
	}
} LOCK;

class SRLock {
public:
	SRLock(LOCK& lock) {
		this->lock = &lock;
		acquired = lock.thread == pthread_self();
		if(!acquired) {
			pthread_mutex_lock(&lock.mutex);
			lock.thread = pthread_self();
		}
	}
	~SRLock() {
		if(!acquired) {
			lock->thread = -1;
			pthread_mutex_unlock(&lock->mutex);
		}
	}
	LOCK *lock;
	bool acquired;
};

typedef struct RWLOCK_st {
	pthread_mutex_t _mutex;
	set<pthread_t> readers;
	pthread_t writer;
	RWLOCK_st() :readers() {
		writer = -1;
		_mutex = PTHREAD_MUTEX_INITIALIZER;
	}
	bool acquired() {
		//LOG << ENDL;
		pthread_mutex_lock(&_mutex);
		pthread_t tno = pthread_self();
		bool ret = writer == tno || readers.find(tno) != readers.end();
		pthread_mutex_unlock(&_mutex);
		return ret;
	}
	void rlock() {
		//LOG << ENDL;
		for(;;) {
			pthread_mutex_lock(&_mutex);
			if(writer == (pthread_t)-1) {
				//LOG << "acquiring read lock " << ENDL;
				readers.insert(pthread_self());
				pthread_mutex_unlock(&_mutex);
				break;
			}
			pthread_mutex_unlock(&_mutex);
			//LOG << "waiting on read lock" << ENDL;
			pthread_yield();
		}
	}
	void wlock() {
		//LOG << ENDL;
		for(;;) {
			pthread_mutex_lock(&_mutex);
			if(readers.empty() && writer == (pthread_t)-1) {
				//LOG << "acquiring wlock " << ENDL;
				writer = pthread_self();
				pthread_mutex_unlock(&_mutex);
				break;
			}
			pthread_mutex_unlock(&_mutex);
			//LOG << "wating on wlock" << ENDL;
			pthread_yield();
		}
	}
	void release() {
		//LOG << ENDL;
		pthread_mutex_lock(&_mutex);
		pthread_t tno = pthread_self();
		if(writer == tno) {
			//LOG << "releasing writer lock" << ENDL;
			writer = -1;
		} else {
			//LOG << "releasing readlock " << ENDL;
			readers.erase(tno);
		}
		pthread_mutex_unlock(&_mutex);
	}
} RWLOCK;

class SRWLock {
public:
	SRWLock(RWLOCK& lock, bool write) {
		this->lock = &lock;
		acquired = lock.acquired();
		if(!acquired) {
			if(write) {
				lock.wlock();
			} else {
				lock.rlock();
			}
		}
	}
	~SRWLock() {
		if(!acquired) {
			lock->release();
		}
	}
	RWLOCK *lock;
	bool acquired;
};

#define SRLOCK SRLock _sl(this->lock);
#define RLOCK SRWLock _srwl(this->lock, false);
#define WLOCK SRWLock _srwl(this->lock, true);

#endif //locking_h
